﻿using System;
using System.IO;
using System.Linq;

using mvcForum.Core;
using mvcForum.Core.Interfaces.Services;
using mvcForum.Core.Interfaces;
using mvcForum.Core.Abstractions.Interfaces;
using mvcForum.Core.Specifications;
using mvcForum.Core.Interfaces.Data;

namespace mvcForum.Web.Services {

	public class AttachmentService : IAttachmentService {
		private readonly IAttachmentStorage storage;
		private readonly IRepository<Attachment> attachmentRepo;
		private readonly IContext context;
		private readonly IConfiguration configuration;
		private readonly IForumAccessService forumAccess;
		private readonly ILogger log;

		public AttachmentService(IAttachmentStorage storage,
								IContext context,
								IConfiguration configuration,
								IForumAccessService fas,
								ILogger log) {

			this.storage = storage;
			this.context = context;
			this.attachmentRepo = this.context.GetRepository<Attachment>();
			this.configuration = configuration;
			this.forumAccess = fas;
			this.log = log;
		}

		public AttachStatusCode AttachFile(ForumUser author, Topic topic, String filename, String contentType, Int32 length, Stream stream) {
			return this.AttachFile(author, topic.Posts.OrderBy(p => p.Posted).First(), filename, contentType, length, stream);
		}

		public AttachStatusCode AttachFile(ForumUser author, Post post, String filename, String contentType, Int32 length, Stream stream) {
			Attachment attachment = null;
			try {
				// Are the user allowed to attach files in this forum?
				if ((this.forumAccess.GetAccessFlag(post.Topic.Forum, author) & AccessFlag.Upload) != AccessFlag.Upload) {
					return AttachStatusCode.PermissionsDenied;
				}

				// Do we have a size limit, and is the file size below the allowed max. size?
				if (this.configuration.MaxFileSize > 0 && this.configuration.MaxFileSize < length) {
					return AttachStatusCode.FileTooBig;
				}

				// Do we have a max. attachment size limit, and is the total size of the user's attachments below the max. allowed size per user?
				if (this.configuration.MaxAttachmentsSize != 0 && (this.configuration.MaxAttachmentsSize < (length + this.GetSpaceUsed(author)))) {
					return AttachStatusCode.NotEnoughSpace;
				}

				// What extension does the file have?
				String extension = Path.GetExtension(filename).ToLower();
				// Let's remove the '.' from the beginning!
				if (extension.StartsWith(".")) {
					extension = extension.Substring(1);
				}
				// Is the file's extension allowed?
				if (!this.configuration.AllowedExtensions.Contains(extension)) {
					return AttachStatusCode.NotAllowedExtension;
				}

				attachment = new Attachment(author, post, "temppath", filename, length);
				this.attachmentRepo.Create(attachment);

				// Save the data, we need the Id generated by the database!
				this.context.SaveChanges();

				// Let's store the bytes/attachment somewhere, we'll get the path back!
				String result = this.storage.CreateAttachment(attachment, stream, contentType);
				// Store the path on the attachment object!
				attachment.Path = result;

				// Throw the updates in the database!
				this.context.SaveChanges();

				// Yay! Mission accomplished!
				return AttachStatusCode.Success;
			}
			catch (Exception ex) {
				// Let's log the error!
				this.log.Log(EventType.Error, "Attaching a file failed", ex);
			}

			// Did a attachment object get created in the database?
			if (attachment != null && attachment.Id > 0) {
				// Yes, let's delete it!
				this.attachmentRepo.Delete(attachment);
				// And update the database!
				this.context.SaveChanges();
			}

			// Not really sure what the error was!
			return AttachStatusCode.Unknown;
		}

		public Int32 GetSpaceUsed(ForumUser user) {
			return this.attachmentRepo.ReadMany(new AttachmentSpecifications.ByAuthor(user)).Sum(a => a.Size);
		}
	}
}